#region imported Namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using System.Text;

using BO = PHSRAG.Insight21.BusinessObjects;
using DataAccess = PHSRAG.Utility.DataAccess;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using SearchCriteria = PHSRAG.Insight21.SearchContext.SearchCriteria;
using GenerateKey = PHSRAG.Insight21.BusinessObjects.ModuleKey;
#endregion

namespace PHSRAG.Insight21.Personnel
{
	#region Class ExpiredFundAllocations
	/// <summary>
	/// The ExpiredFundAllocations object supports the loading of loading the expired fund allocation
	/// data the user has access to. It also supports sorting and searching on the retrieved data.
	/// </summary>
	public class ExpiredFundAllocations:BO.BusinessObject
	{
		#region Constants
		private static string	ExpiredFundAllocationsKey	= GenerateKey.MakeKey("ExpiredFundAllocations_88226B47-1EDF-4CE4-AD0F-F393D0F2332D",GenerateKey.Personnel);
		private const int		DomainUserIdArgumentIndex	= 0;
		private const string	DefaultSortColumn			= "ProjectEndDate";
		private const int 		AllocationDataTableIndex	= 0;
		private const int ProjectEndingFilterAlreadyExpired = -1;
		#endregion

		#region Instance Variables
		private Hashtable		expiredFundAllocationMap	= new Hashtable();
		private string			sortColumn					= DefaultSortColumn;		
		private string			currentDataTableName		= Constants.OriginalDataTableName;
		private bool			sortAscending				= true;
		#endregion

		#region Constructor
		/// <summary>
		/// Constructor, also intializes the base class with the prevalent cache policy
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		public ExpiredFundAllocations() : base(null)
		{
		}
		#endregion

		#region Public Properties
		/// <summary>
		/// Get the key to be used in all caching operations for this instance of the object.
		/// </summary>
		public static string CacheKey	{ get { return ExpiredFundAllocationsKey; } }
		/// <summary>
		/// Get the total number of expired fund allocation in the data container.
		/// Returns 0 if there is no data in the data container
		/// </summary>
		public int Count
		{
			get { return (DataContainer.Tables.Count == 0) ? 0 : DataContainer.Tables[currentDataTableName].Rows.Count; }
		}
		/// <summary>
		/// Get the sort sequence.
		/// </summary>
		public bool SortAscending
		{
			get { return sortAscending; }
		}

		/// <summary>
		/// Get the name of the sort column.
		/// </summary>
		public string SortColumn
		{
			get { return sortColumn; }
		}
		/// <summary>
		/// Get the table name of the active table in the data container
		/// This will be used for selective downloading of data into reports
		/// </summary>
		public string CurrentDataTableName
		{
			get { return currentDataTableName;}
		}
		#endregion

		#region BusinessObject Methods
		/// <summary>
		/// Load the expired fund allocation data for the currently logged in user and set the datatable name
		/// For the load the domain user id is mandatory and it is the only argument required.
		/// Sort the laoded data on the default sort column
		/// </summary>
		/// <param name="connectionString">Database connection string</param>
		/// <param name="args">Is expected to contain a single argument - the domain user ID</param>
		public override void Load(string connectionString, params object[] args)
		{
			if (args.Length != 1)
				throw new Exception("Expired fund allocations load requires a domain user ID");

			DataContainer =	DataAccess.GetDataSet(
				connectionString
				,Constants.ExpiredFundSPROCName
				,DataAccess.BuildSqlParameterArray("@domainUserID", SqlDbType.Int, (int)args[DomainUserIdArgumentIndex]));

			if(DataContainer.Tables.Count == 1)
                DataContainer.Tables[AllocationDataTableIndex].TableName = Constants.OriginalDataTableName;

			ProcessSort();
		}
		#endregion

		#region Public methods
		/// <summary>
		/// Get the list of ExpiredFundAllocation objects bound by a start and end index. 
		/// These indeces are into the list of expired fund allocations that was loaded during the 
		/// Load() method execution into the data container. Typically, the start index is on 
		/// a "page" boundary, where the "page" referes to a datagrid page.
		/// A Hashtable of ArrayList objects is maintained using the start index as the key. 
		/// This allows retrieval of the set of ExpiredFundAllocation objects associated with that 
		/// index, thereby directly supporting random access based the page index.
		/// </summary>
		/// <param name="startIndex">Index within the expired fund allocation list to begin at</param>
		/// <param name="count">Total number of ExpiredFundAllocation objects to load</param>
		/// <returns>The list of ExpiredFundAllocation objects</returns>
		public ArrayList GetAllocationList(int startIndex,int count)
		{
			ArrayList allocationList = expiredFundAllocationMap[startIndex] as ArrayList;
			if (allocationList == null)
			{
				allocationList = new ArrayList();
				DataTable dataTable = DataContainer.Tables[currentDataTableName];
				for (int indexCount = startIndex, end = Math.Min(indexCount + count, dataTable.Rows.Count); 
					indexCount < end; ++indexCount)
					allocationList.Add(new ExpiredFundAllocation(CachePolicy, dataTable.Rows[indexCount]));
				expiredFundAllocationMap[startIndex] = allocationList;
			}
			return allocationList;
		}
		/// <summary>
		/// Sort the list of fund allocations by the speicified column.
		/// If the column is to be sorted is the same as what was last sorted on, the sort sequence is toggled. Else, the
		/// sort sequence is set to ascending.
		/// </summary>
		/// <param name="columnName">Column to sort by</param>
		public void Sort(string columnName)
		{
			expiredFundAllocationMap.Clear();

			sortAscending = (sortColumn == columnName) ? !sortAscending : true;
			sortColumn = columnName;

			ProcessSort();
		}
		/// <summary>
		/// Search the DataContainer for rows which match the specified search criteria.
		/// Two DataTables are maintained within the DataContainer; a full-complement DataTable and a filtered one.
		/// This allows filtering without having to hit the database. If a filter is in
		/// effect, we simply set the value of the currentTableName to Constants.FilteredDataTableName so that the
		/// GetAllocationList() method may retreive from the appropriate DataTable.
		/// </summary>
		/// <param name="searchCriteria">The search criteria instance picked from the search context</param>
		/// <remarks>True if the search criteria did any filtering; false otherwise</remarks>
		public bool Search(SearchCriteria searchCriteria)
		{
			expiredFundAllocationMap.Clear();

			StringBuilder filter = new StringBuilder();
			foreach (string key in searchCriteria.Keys)
			{
				switch (key.ToLower())
				{
					case "employeefirstname":
						filter.AppendFormat("{0} FirstName LIKE '{1}%'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "employeelastname":
						filter.AppendFormat("{0} LastName LIKE '{1}%'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;					
					case "effectivesalarydatefrom":
						filter.AppendFormat("{0} AllocationStartDate>='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "effectivesalarydateto":
						filter.AppendFormat("{0} AllocationEndDate<='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "viewbyprojectswithin":
						int projectEndingDate = Convert.ToInt32(searchCriteria[key]);
						filter.AppendFormat("{0} {1}",(filter.Length>0)?"AND":"",
							(projectEndingDate == ProjectEndingFilterAlreadyExpired)
							?string.Format("ProjectEndDate<='{0}'"
							,DateTime.Now.AddDays(projectEndingDate).ToString("MM/dd/yyyy"))
							:string.Format("ProjectEndDate>='{0}' AND ProjectEndDate<='{1}'"
							,DateTime.Now.ToString("MM/dd/yyyy")
							,DateTime.Now.AddDays(projectEndingDate).ToString("MM/dd/yyyy")));
						break;
					case "fundnumber":
						filter.AppendFormat("{0} FundNumber LIKE '{1}%'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
					case "pi":
						filter.AppendFormat("{0} PIID ='{1}'",(filter.Length>0)?"AND":"",searchCriteria[key]);
						break;
				}
			}

			bool applySearch = (filter.Length > 0); 
			if(applySearch)
			{
				DataTable allocationTable = DataContainer.Tables[Constants.OriginalDataTableName];
				DataTable filteredDataTable = allocationTable.Clone();
				filteredDataTable.TableName = currentDataTableName = Constants.FilteredDataTableName;

				DataRow[] filteredRows = allocationTable.Select(filter.ToString());
				foreach (DataRow allocationRow in filteredRows)
					filteredDataTable.ImportRow(allocationRow);

				if (DataContainer.Tables.Contains(Constants.FilteredDataTableName))
					DataContainer.Tables.Remove(Constants.FilteredDataTableName);
				DataContainer.Tables.Add(filteredDataTable);
			}
			else
				currentDataTableName = Constants.OriginalDataTableName;

			return applySearch;
		}
		#endregion

		#region Private methods
		/// <summary>
		/// Sorts the current data table on the sortColumn instance variable
		/// in the order specified by the sortOrder instance variable
		/// </summary>
		private void ProcessSort()
		{
			DataTable allocationTable = DataContainer.Tables[currentDataTableName];
			DataView allocationView = allocationTable.DefaultView;
			allocationView.Sort = string.Format("{0} {1}", 
				sortColumn, sortAscending ? Constants.SortOrderAscending : Constants.SortOrderDescending);

			DataTable sortedDataTable = allocationTable.Clone();
			foreach (DataRowView allocationRowView in allocationView)
				sortedDataTable.ImportRow(allocationRowView.Row);

			if(DataContainer.Tables.Contains(currentDataTableName))
				DataContainer.Tables.Remove(currentDataTableName);

			DataContainer.Tables.Add(sortedDataTable);
		}
		#endregion
	}
	#endregion
}